local class = reload("class")
local super = reload("super.basic")
local tclone = reload("table.temp.clone")
local tqueue = reload("vessel.tqueue")
local getZero = reload("time.getZero")
local getWhatDay = reload("time.getWhatDay")
local ctimer = require("single.ctimer")

---重复使用仓库
local store = reload("store")
local newt, delt = store()

---@class timer:super.basic @定时管理器
local this = class(super)

---构造
function this:ctor()
    super.ctor(self)
    self._start = false
    ---清理处理
    self:clear()
    ---定时函数
    self:launch()
end

---启动函数
function this:launch()
    ---避免重复启动
    if self._start then
        return
    end
    self._start = true
    ---启动定时器
    self:register()
end

---清除数据
function this:clear()
    ---@type integer                @暂停时间
    self._pauset = nil
    ---最小堆
    self._sort = tqueue.new()
    ---定时器
    ---@type table<string,number>
    self._names = {}
    ---定时器
    ---@type table<number,string>
    self._idens = {}
end

function this:gcback()
    super.gcback(self)
    ctimer.unregister(self)
end

---当前微妙
function this:nowmcs()
    if not self._nowmcs then
        self._nowmcs = other.nowmcs()
    end
    return self._nowmcs
end

---当前毫秒
function this:nowmls()
    if not self._nowmls then
        self._nowmls = other.nowmls()
    end
    return self._nowmls
end

---当前秒钟
function this:nowsec()
    if not self._nowsec then
        self._nowsec = other.nowsec()
    end
    return self._nowsec
end

function this:create(elapse, count, call, ...)
    local item = newt()
    item.elapse = elapse
    item.count = count
    item.call = call
    item.args = select("#", ...) > 0 and { ... } or nil
    return item
end

function this:nameCheck(key, auto)
    if not auto then
        return false
    end

    ---删除之前定时器
    local rid = self._names[key]
    if rid then
        self:removeByID(rid)
    end

    ---记录新的定时器
    self._names[key] = auto
    self._idens[auto] = key
    return true
end

---定时回调
function this:append(elapse, count, call, ...)
    local sort = self._sort
    local item = self:create(elapse, count, call, ...)
    local tick = self:nowmls() + elapse
    local node = sort:create(tick, item)
    return sort:push_back(node, false)
end

---单次回调
function this:appendCall(elapse, call, ...)
    return self:append(elapse, 1, call, ...)
end

---无限回调
function this:appendEver(ms, call, ...)
    return self:append(ms, nil, call, ...)
end

---无限回调多少秒
function this:secEver(sec, call, ...)
    return self:append(sec * 1000, nil, call, ...)
end

---无限回调1分钟
function this:minuteEver(min, call, ...)
    return self:append(min * 60000, nil, call, ...)
end

---无限回调1小时
function this:hourEver(hour, call, ...)
    return self:append(hour * 3600000, nil, call, ...)
end

---定时回调
function this:appendBy(name, elapse, count, call, ...)
    local iden = self:append(elapse, count, call, ...)
    self:nameCheck(name, iden)
    return iden
end

---单次回调
function this:appendFirstBy(name, ms, call, ...)
    return self:appendBy(name, ms, 1, call, ...)
end

---1秒钟单次回调
function this:secFirstBy(name, sec, call, ...)
    return self:appendBy(name, sec * 1000, 1, call, ...)
end

---1分钟单次回调
function this:minuteFirstBy(name, min, call, ...)
    return self:appendBy(name, min * 60000, 1, call, ...)
end

---1小时单次回调
function this:hourFirstBy(name, hour, call, ...)
    return self:appendBy(name, hour * 3600000, 1, call, ...)
end

---无限回调
function this:appendEverBy(name, ms, call, ...)
    return self:appendBy(name, ms, nil, call, ...)
end

---定点回调
function this:dayEvery(hour, min, sec, call, ...)
    local now = self:nowsec()
    local zsec = getZero(now)
    local nexsec = hour * 3600 + min * 60 + sec
    local daysec = zsec + nexsec
    if daysec < now then
        ---今天时间已经过了
        daysec = daysec + 86400
    end

    ---添加执行事件
    local firstElapse = (daysec - now) * 1000
    local ulti        = self:nowmls() + firstElapse
    local sort        = self._sort
    local item        = self:create(86400000, nil, call, ...)
    local node        = sort:create(ulti, item)
    sort:push_back(node, false)
    return node.auto
end

---定点回调
function this:dayEveryBy(name, hour, min, sec, call, ...)
    local auto = self:dayEvery(hour, min, sec, call, ...)
    self:nameCheck(name, auto)
    return auto
end

---今天定点单次回调
function this:dayFirstCall(hour, min, sec, call, ...)
    return self:dayNextCall(0, hour, min, sec, call, ...)
end

---下次天数定点回调
function this:dayNextCall(day, hour, min, sec, call, ...)
    if day < 0 then
        error("day must be greater than or equal to 0")
        return
    end

    local now = self:nowsec()
    local non = getZero(now)
    local new = non + (day * 86400) + (hour * 3600) + (min * 60) + sec
    local dsec = new - now
    if new < now then
        return -1
    end
    return self:append(dsec * 1000, 1, call, ...)
end

---定点回调()
function this:dayEveryBys(list, call, ...)
    for _, info in ipairs(list) do
        self:dayEveryBy(info.name, info.hour, info.min, info.sec, call, ...)
    end
end

---本周定点单次回调
function this:weekFirstCall(week, hour, min, sec, call, ...)
    local now = self:nowsec()
    local wek = getWhatDay(now)
    local day = week - wek
    if day < 0 then
        return -1
    end
    return self:dayNextCall(day, hour, min, sec, call, ...)
end

---周定时回调
function this:weekEveryBy(name, week, hour, min, sec, call, ...)
    local auto = self:weekEvery(week, hour, min, sec, call, ...)
    self:nameCheck(name, auto)
    return auto
end

---周定时回调-固定偏移增加多个定时器
function this:weekOffsets(info, call, ...)
    local offset = info.offset or 1
    local idens = newt()
    local tinfo = tclone(info)
    for i = 1, offset do
        ---记录生成定时器
        local id, node = self:weekEvery(tinfo.wday, tinfo.hour, tinfo.min, tinfo.sec, call, ...)
        idens[id]     = i
        ---如果指定次数了
        node.data.count = tinfo.count
        ---增加偏移值
        tinfo.wday = tinfo.wday + (tinfo.iwday or 0)
        tinfo.hour = tinfo.hour + (tinfo.ihour or 0)
        tinfo.min  = tinfo.min + (tinfo.imin or 0)
        tinfo.sec  = tinfo.sec + (tinfo.isec or 0)
    end
    return idens
end

---周定时回调
function this:weekEvery(week, hour, min, sec, call, ...)
    local now = self:nowsec()
    local zsec = getZero(now)          -- 获取今天的零点时间
    local dayOfWeek = getWhatDay(now) -- 获取今天是周几 (1 = 周日, 2 = 周一, ..., 7 = 周六)

    -- 计算目标周几的日期
    local targetDay = week                    -- 将周日调整为 1，周一为 2，...，周六为 7
    local daysUntilNext = (targetDay - dayOfWeek + 7) % 7 -- 计算到下一个目标周几的天数

    -- 如果今天就是目标周几，且时间已经过了，则需要加 7 天
    if daysUntilNext == 0 then
        local targetTime = zsec + (hour * 3600 + min * 60 + sec)
        if now > targetTime then
            daysUntilNext = 7
        end
    end

    -- 计算下一个目标时间的秒数
    local nextTargetTime = zsec + (daysUntilNext * 86400) + (hour * 3600 + min * 60 + sec)
    ---添加执行事件
    local firstElapse    = (nextTargetTime - now) * 1000 -- 转换为毫秒
    local ulti           = self:nowmls() + firstElapse
    local sort           = self._sort
    local item           = self:create(604800000, nil, call, ...)
    local node           = sort:create(ulti, item)
    sort:push_back(node, false)
    return node.auto, node
end

---删除定时
function this:removeByID(iden)
    local idens = self._idens
    local names = self._names
    local tnode = self._sort:auto_move(iden)
    if tnode then
        ---回收数据
        delt(tnode.data)
        self._sort:delt(tnode)

        ---清除标识
        idens[iden] = nil

        ---全局标识
        local name = idens[iden]
        if name then
            names[name] = nil
        end
    end
end

---删除头部
function this:removeFirst(reder)
    local idens = self._idens
    local names = self._names

    if reder then
        ---获取标识
        local iden = reder.auto
        ---回收数据
        delt(reder.data)
        self._sort:delt(reder)

        ---清除标识
        idens[iden] = nil

        ---全局标识
        local name = idens[iden]
        if name then
            names[name] = nil
        end
    end
end

---删除定时
function this:removeByName(name)
    local iden = self._names[name]
    if not iden then
        return
    end
    return self:removeByID(iden)
end

---剩余时间
function this:remaining(iden)
    local now = self._pauset or self:nowmls()
    local node = self._sort:find_node(iden)
    if node then
        return node.ticks - now
    end
    return 0
end

---剩余时间
function this:remainingBy(name)
    local names = self._names
    local iden = names[name]
    return self:remaining(iden)
end

---暂停定时
function this:pause()
    if not self._pauset then
        self._pauset = self:nowmls()
    end
end

---暂停取消
function this:unpause()
    local pause = self._pauset

    if pause then
        self._pauset = nil
        local now = self:nowmls()
        local dif = now - pause

        ---触发时间+偏移值+暂停的时间永远失去了
        for _, link in ipairs(self._sort._uniqmap) do
            link.data.ticks = link.data.ticks + dif
        end

        --启动轮询
        self:callback(self:nowmls())
    end
end

---定时轮询
function this:callback(nowms)
    self._nowmcs = nowms * 1000
    self._nowmls = nowms
    self._nowsec = nowms // 1000
    ---暂停
    if self._pauset then
        return
    end

    ---执行
    repeat
        if not self:execute(nowms) then
            break
        end
    until false
end

---轮询器
function this:register()
    ctimer.register(self)
end

---检测器
---@param inow number @当前时间
---@param iend number @结束时间
function this:ifExpire(inow, iend)
    if inow < iend then
        return false
    end

    return true
end

---执行
---@return boolean
function this:execute(nowms)
    local csort = self._sort
    local reder = csort:reder()

    if not reder then
        return false
    end

    ---@type number         @时间
    local inow = nowms
    local iend = reder.ticks

    ---到期检查
    if not self:ifExpire(inow, iend) then
        return false
    end

    ---@type tagTimer   @定时器
    local item = reder.data
    ---@type number      @多少次
    local count = item.count
    if count then
        ---扣除次数
        count = count - 1
        item.count = count
    end

    ---每次只调用一个定时
    local args = item.args
    local cmd = ""
    if not args then
        cmd = item.call()
    else
        cmd = item.call(table.unpack(args))
    end

    ---删除定时器
    if csort:find_move(reder) then
        if 0 == count then
            ---不添加了
        elseif cmd == "delete" then
            ---不添加了
        else
            ---重新添加
            reder.ticks = reder.ticks + item.elapse
            csort:push_back(reder, false)
        end
    end
    return true
end

return this

---@class tagTimer              @定时数据
---@field elapse    number      @间隔时间
---@field count     number      @回调次数
---@field call      fun()       @回调函数
---@field args      any[]       @回调参数
---@field cmd       string|nil  @执行指令
